package dao

import (
	"github.com/astaxie/beego/orm"
	"github.com/pkg/errors"
	"github.com/spf13/cast"
	"nft-server/common"
	"nft-server/model"
)

const (
	DefaultUserId = -1
)

type UserDao struct {
	BaseDao
}

type IUserDao interface {
	SaveUserInfo(user *model.User) error

	GetAdminUser() (admin *model.User, err error)
	GetUserInformation(queryParameter *common.QueryUserDetail) (*model.User, error)
	GetUserNum(queryNumParameter *common.QueryUserNum) (int, error)
	GetUserList(queryUserListParameter *common.QueryUserList) (users []*model.User, err error)

	UpdateAllUserInfo(modifyParameter *common.ModifyUserDetail) error
	UpdateUserInfo(user *model.User) error

	DeleteUserInfo(userId int) error
	CountAdmin() (int, error)
	CheckPhoneNumUsed(phoneNumber string) (bool, error)
}

func GetUserDao() IUserDao {
	dao := &UserDao{}
	dao.Init()
	return dao
}

func (ud *UserDao) SaveUserInfo(user *model.User) error {
	_, err := ud.O.Insert(user)
	if err != nil {
		return errors.Errorf("insert error: %v", err)
	}
	return nil
}

func (ud *UserDao) GetUserInformation(queryParameter *common.QueryUserDetail) (*model.User, error) {
	user := new(model.User)
	recode := ud.O.QueryTable(&model.User{})
	finalCondition := orm.NewCondition()
	if queryParameter.UserId != nil {
		finalCondition = finalCondition.And("id", *queryParameter.UserId)
	}
	if queryParameter.UserName != nil {
		finalCondition = finalCondition.And("name", *queryParameter.UserName)
	}
	if queryParameter.Address != nil {
		finalCondition = finalCondition.And("address", *queryParameter.Address)
	}
	if queryParameter.PhoneNumber != nil {
		finalCondition = finalCondition.And("phone_number", *queryParameter.PhoneNumber)
	}
	recode = recode.SetCond(finalCondition).RelatedSel()
	err := recode.One(user)
	if err != nil {
		if err == orm.ErrNoRows {
			return nil, nil
		}
		return nil, errors.Errorf("read error: %v", err)
	}
	return user, nil
}

func (ud *UserDao) UpdateUserInfo(user *model.User) error {
	_, err := ud.O.Update(user)
	if err != nil {
		return errors.Errorf("update error: %v", err)
	}
	return nil
}

func (ud *UserDao) DeleteUserInfo(userId int) error {
	user := new(model.User)
	user.Id = userId
	_, err := ud.O.Delete(user, "id")
	if err != nil {
		return errors.Errorf("delete error: %v", err)
	}
	return nil
}

func (ud *UserDao) GetUserList(queryListParameter *common.QueryUserList) (users []*model.User, err error) {
	recode := ud.O.QueryTable(&model.User{})
	finalCondition := orm.NewCondition()
	if queryListParameter.UserName != nil {
		finalCondition = finalCondition.And("name", *queryListParameter.UserName)
	}
	if queryListParameter.Locked != nil {
		finalCondition = finalCondition.And("locked", *queryListParameter.Locked)
	}
	recode = recode.SetCond(finalCondition)
	_, err = recode.Offset(queryListParameter.Offset).Limit(queryListParameter.PageSize).RelatedSel().All(&users, "id", "name", "locked", "role_id", "authenticated")
	if err != nil {
		return nil, errors.Errorf("GetUserList error: %v", err)
	}
	return users, err
}

func (ud *UserDao) GetUserNum(queryNumParameter *common.QueryUserNum) (int, error) {
	recode := ud.O.QueryTable(&model.User{})
	finalCondition := orm.NewCondition()
	if queryNumParameter.UserName != nil {
		finalCondition = finalCondition.And("name", *queryNumParameter.UserName)
	}
	if queryNumParameter.Locked != nil {
		finalCondition = finalCondition.And("locked", *queryNumParameter.Locked)
	}
	recode = recode.SetCond(finalCondition)
	totalNum64, err := recode.Count()
	if err != nil {
		return 0, errors.Errorf("GetUserNum error: %v", err)
	}
	totalNum := cast.ToInt(totalNum64)
	return totalNum, nil
}

func (ud *UserDao) GetAdminUser() (*model.User, error) {
	admin := &model.User{}
	err := ud.O.QueryTable("user").Filter("role_id", 1).One(admin)
	if err != nil {
		return nil, errors.Errorf("GetAdminUser error: %v", err)
	}
	return admin, nil
}

func (ud *UserDao) CountAdmin() (int, error) {
	totalNum64, err := ud.O.QueryTable(new(model.User)).Filter("role_id", 1).Count()
	if err != nil {
		return 0, errors.Errorf("GetUserNum error: %v", err)
	}
	totalNum := cast.ToInt(totalNum64)
	return totalNum, nil
}

func (ud *UserDao) CheckPhoneNumUsed(phoneNum string) (bool, error) {
	user := new(model.User)
	err := ud.O.QueryTable(new(model.User)).Filter("phone_number", phoneNum).RelatedSel().One(user)
	if err != nil {
		if err == orm.ErrNoRows {
			return false, nil
		}
		return true, errors.Errorf("read error: %v", err)
	}
	return true, nil
}

func (ud *UserDao) UpdateAllUserInfo(modifyParameter *common.ModifyUserDetail) error {
	modify := orm.Params{}
	if modifyParameter.Password != nil {
		modify["password"] = *modifyParameter.Password
		modify["salt"] = *modifyParameter.Salt
		modify["pwdchanged"] = true
	}
	if modifyParameter.Authenticated != nil {
		modify["authenticated"] = *modifyParameter.Authenticated
	}
	if modifyParameter.AssetObtainedNum != nil {
		modify["asset_obtained_num"] = *modifyParameter.AssetObtainedNum
	}
	if modifyParameter.ProfilePhotoUrl != nil {
		modify["profile_photo_url"] = *modifyParameter.ProfilePhotoUrl
	}
	if modifyParameter.Lock != nil {
		modify["locked"] = *modifyParameter.Lock
	}
	if modifyParameter.FailTimes != nil {
		modify["fail_times"] = *modifyParameter.FailTimes
	}
	if modifyParameter.LockTime != nil {
		modify["locked_time"] = *modifyParameter.LockTime
	}
	if modifyParameter.PhoneNumber != nil {
		modify["phone_number"] = *modifyParameter.PhoneNumber
	}

	if len(modify) != 0 {
		_, err := ud.O.QueryTable(&model.User{}).Filter("id", modifyParameter.UserId).Update(modify)
		if err != nil {
			return errors.Errorf("update error: %v", err)
		}
	}
	return nil

}
